package com.tbit.main.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.PatternPool;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tbit.main.constant.*;
import com.tbit.main.constant.enums.DispatchApplyHandleType;
import com.tbit.main.dao.log.DispatchApplyLogMapper;
import com.tbit.main.exception.BaseException;
import com.tbit.main.h2.pojo.ParkPoint;
import com.tbit.main.pojo.*;
import com.tbit.main.pojo.VO.DispatchApplyLogStat;
import com.tbit.main.pojo.VO.DispatchApplyLogVo;
import com.tbit.main.service.*;
import com.tbit.main.util.DateTimeUtil;
import com.tbit.main.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: chen
 * @Description: 调度申请
 * @Date: 创建于 15:44 2023/2/10
 */
@Service
@Slf4j
public class DispatchApplyLogServiceImpl extends ServiceImpl<DispatchApplyLogMapper, DispatchApplyLog> implements
        DispatchApplyLogService {

    @Autowired
    private DispatchApplyLogMapper dispatchApplyLogMapper;
    @Autowired
    private CalibrateService calibrateService;
    @Autowired
    private DispatchApplyMachineService dispatchApplyMachineService;
    @Autowired
    private MachineService machineService;
    @Autowired
    private StockMachineService stockMachineService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private MachineOccupyService machineOccupyService;
    @Autowired
    private ParkPointService parkPointService;
    @Autowired
    private DataService dataService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private AccountConfigService accountConfigService;
    @Autowired
    private ImageService imageService;
    @Autowired
    private TerControlService terControlService;

    private static final Integer OCCUPY_NO_LIMIT = -999;

    @Override
    public List<DispatchApplyLogVo> getApplyLogStat(AccountUser accountUser, Integer accountId, Long startTime, Long endTime) {

        List<DispatchApplyLogStat> dispatchApplyLogStats = groupByAccountIdState(accountUser, accountId, startTime, endTime);

        List<DispatchApplyLogVo> dispatchApplyLogVos = new ArrayList<>();
        Map<Integer, DispatchApplyLogVo> map = new HashMap<>();
        /**构造数据*/
        for (DispatchApplyLogStat dispatchApplyLogStat : dispatchApplyLogStats) {
            DispatchApplyLogVo dispatchApplyLogVo = new DispatchApplyLogVo("", 0, 0, 0, 0, 0);

            if (map.containsKey(dispatchApplyLogStat.getApplyUserId())) {
                dispatchApplyLogVo = map.get(dispatchApplyLogStat.getApplyUserId());
            }
            dispatchApplyLogVo.setApplyUserId(dispatchApplyLogStat.getApplyUserId());
            dispatchApplyLogVo.setApplyUserName(dispatchApplyLogStat.getApplyUserName());
            dispatchApplyLogVo.setTotal(dispatchApplyLogStat.getNum() + dispatchApplyLogVo.getTotal());
            if (dispatchApplyLogStat.getState() == DispatchApplyLogConstant.DEFAULTSTATE) {
                dispatchApplyLogVo.setDefaultNum(dispatchApplyLogVo.getDefaultNum() + dispatchApplyLogStat.getNum());
            } else if (dispatchApplyLogStat.getState() == DispatchApplyLogConstant.SUCCESSSTATE) {
                dispatchApplyLogVo.setSuccessNum(dispatchApplyLogVo.getSuccessNum() + dispatchApplyLogStat.getNum());
            } else {
                dispatchApplyLogVo.setFailNum(dispatchApplyLogVo.getFailNum() + dispatchApplyLogStat.getNum());
            }
            map.put(dispatchApplyLogStat.getApplyUserId(), dispatchApplyLogVo);
        }
        if (!map.isEmpty()) {
            for (Integer id : map.keySet()) {
                dispatchApplyLogVos.add(map.get(id));
            }
        }
        return dispatchApplyLogVos;
    }

    @Override
    public List<DispatchApplyLogStat> groupByAccountIdState(AccountUser accountUser, Integer accountId, Long startTime,
                                                            Long endTime) {

        /**查询代理商运维账号*/
        List<Integer> accountUserIds = new ArrayList<Integer>();
        /**判断当前账号是不是运维人员*/
        if (roleService.getRoleCode(accountUser.getAccountUserType()).equals(1)) {
            accountUserIds.add(accountUser.getAccountUserId());
        }
        Map<String, Object> map = new HashMap<>();
        map.put("accountId", accountId);
        map.put("startTime", new Date(startTime));
        map.put("endTime", new Date());
        map.put("accountUserIds", StringUtil.getListString(accountUserIds));
        return dispatchApplyLogMapper.groupByAccountIdState(map);
    }

    @Override
    public List<DispatchApplyLog> getByIdAndTime(Integer accountUserId, Long startTime,
                                                 Long endTime, Integer mapType, Integer accountId) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("applyUserId", accountUserId);
        queryWrapper.eq("accountId", accountId);
        queryWrapper.ge("applyTime", new Date(startTime));
        queryWrapper.le("applyTime", new Date(endTime));
        List<DispatchApplyLog> dispatchApplyLogs = list(queryWrapper);
        if (dispatchApplyLogs.size() > 0) {
            dispatchApplyLogs.stream().forEach(d -> {
                Point point = calibrateService.commonCalibrate(new Point(d.getLo(), d.getLa()), MapConstant.MAP_ORI, mapType);
                d.setLo(point.getX());
                d.setLa(point.getY());
            });
        }
        return dispatchApplyLogs;
    }

    @Override
    public List<DispatchApplyMachine> getByApplyId(Integer applyId) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("applyId", applyId);
        List<DispatchApplyMachine> dispatchApplyMachines = dispatchApplyMachineService.list(queryWrapper);
        return dispatchApplyMachines;
    }

    @Override
    @Transactional(value = "logTransactionManager")
    public void addBatch(List<String> userCodes, String reason, Double lon, Double lat,
                         AccountUser accountUser, Integer mapType, List<String> photoUrl, boolean isNew) throws Exception {
        userCodesCheck(userCodes);

        List<Machine> machines = machineService.getByUserCodes(userCodes);
        /**
         * 权限校验
         * checkBatch会修改userCodes的值，所以这里需要重新new一个
         *
         **/
        stockMachineService.checkBatch(machines, accountUser, new ArrayList<>(userCodes));


        List<Integer> machineIds = machines.stream().map(Machine::getMachineId).collect(Collectors.toList());

        /**是否是未出库车辆*/
        List<Integer> stockMachineIds = stockMachineService.getAccountIdByMachineId(machines.get(0).getAccountId(), machineIds);
        if (stockMachineIds != null && !stockMachineIds.isEmpty()) {
            throw new BaseException("StockMachine.is.operate");
        }

        List<MachineOccupy> machineOccupies = machineOccupyService.getAccountUserId(machineIds);
        if (CollectionUtil.isNotEmpty(machineOccupies)){
            throw new BaseException("WorkOrder.occupy.notOperate");
        }

        Integer accountId = machines.get(0).getAccountId();
        if (isNew) {
            photoCheck(accountId, photoUrl);
        }

        /**查询已申请车辆*/
        List<Integer> machineStateIds = dispatchApplyMachineService.getStateMachineId(machineIds, accountId);
        if (machineStateIds != null && !machineStateIds.isEmpty()) {
            throw new BaseException("DispatchApplyMachine.is.exist");
        }

        // 校验车辆是否已被调度
        List<String> machineNos = machines.stream().map(Machine::getMachineNO).collect(Collectors.toList());
        List<String> dispatchingMachineNos = extratDispatchingMachineNos(machineNos);
        if (CollectionUtil.isNotEmpty(dispatchingMachineNos)) {
            throw new BaseException("machine.already.dispatching");
        }


        Date date = new Date();
        /**坐标校准*/
        Point point = calibrateService.commonCalibrate(new Point(lon, lat), mapType, MapConstant.MAP_ORI);
        DispatchApplyLog dispatchApplyLog = new DispatchApplyLog(accountId, machines.size(), date, point.getX()
                , point.getY(), reason, accountUser.getAccountUserId(), accountUser.getName(), DispatchApplyLogConstant.DEFAULTSTATE);
        dispatchApplyLog.setApplyUserPhone(accountUser.getPhone());
        if (CollectionUtil.isNotEmpty(photoUrl)){
            dispatchApplyLog.setPhotoUrl(photoUrl);
        }
        dispatchApplyLogMapper.add(dispatchApplyLog);
        Integer applyId = dispatchApplyLog.getApplyId();
        List<DispatchApplyMachine> dispatchApplyMachines = new ArrayList<>();
        for (Machine machine : machines) {
            DispatchApplyMachine dispatchApplyMachine = new DispatchApplyMachine(accountId, machine.getMachineId(), machine.getUserCode(), machine.getMachineNO()
                    , date, applyId, "");
            dispatchApplyMachines.add(dispatchApplyMachine);
        }
        dispatchApplyMachineService.saveBatch(dispatchApplyMachines);
        boolean autoAuditSuccess = autoAuditSuccess(accountId,applyId);
        if (autoAuditSuccess){
            //独立事务
            terControlService.controlBatch(accountUser, userCodes, null, mapType, lon, lat, null, null, null, ControlType.MACHINE_CONTROL_UNLOCK);
        }


    }

    /**
     * 自动审核通过
     *
     * @param accountId
     * @param applyId
     */
    private boolean autoAuditSuccess(Integer accountId,Integer applyId){
        boolean dispatchApplyAutoAuditSwitchOpen = Optional.ofNullable(accountConfigService.getByAccountId(accountId))
                                                           .map(AccountConfig::getDispatchApplyAutoAuditSwitch).orElse(0) == 1;
        if (dispatchApplyAutoAuditSwitchOpen){
            String remark = "自动审核";
            AccountUser accountUser = new AccountUser();
            accountUser.setAccountUserId(-1);
            accountUser.setName("系统");
            log.info("自动审核通过 {}",applyId);
            return handle(applyId, DispatchApplyLogConstant.SUCCESSSTATE, remark, accountUser,DispatchApplyHandleType.SYSTEM);
        }
        return false;
    }

    /**
     * 提取正在调度的车辆
     *
     * @param machineNos
     * @return
     */
    private List<String> extratDispatchingMachineNos(List<String> machineNos) {
        if (CollectionUtil.isEmpty(machineNos)) {
            return Collections.EMPTY_LIST;
        }
        List<String> dispatchingMachineNos = new ArrayList<>();
        for (String machineNo : machineNos) {
            if (StringUtils.isNotBlank(redisService.get(RedisConstant.REDIS_TER_BIKE_DISPATCH + machineNo))) {
                dispatchingMachineNos.add(machineNo);
            }
        }
        return dispatchingMachineNos;
    }

    private void userCodesCheck(List<String> userCodes) {
        if (CollectionUtils.isEmpty(userCodes)) {
            throw new BaseException("Machine.userCode.notNull");
        }
        for (int i = 0; i < userCodes.size(); i++) {
            String oldStr = userCodes.get(i);
            String trimVal = oldStr.trim();
            if (!PatternPool.NUMBERS.matcher(trimVal).matches()) {
                throw new BaseException("车辆编号[" + oldStr + "]格式错误,车辆编号只能为纯数字");
            }
            userCodes.set(i, trimVal);
        }
    }

    private void photoCheck(Integer accountId,List<String> photourl) {
        boolean dispatchApplyPhotoSwitchOpen = Optional.ofNullable(accountConfigService.getByAccountId(accountId))
                                                       .map(AccountConfig::getDispatchApplyPhotoSwitch).orElse(0) == 1;
        if (dispatchApplyPhotoSwitchOpen) {
            if (CollectionUtil.isEmpty(photourl)) {
                throw new BaseException("人工调度申请上传照片开关开启时至少需要上传一张图片");
            }
            if (photourl.size() > 3) {
                throw new BaseException("人工调度申请上传照片开关开启时最多上传三张图片");
            }
        }
    }

    @Override
    public boolean handle(Integer applyId, Integer state, String remark, AccountUser accountUser, DispatchApplyHandleType handleType) {

        DispatchApplyLog dispatchApplyLog = getById(applyId);
        if (dispatchApplyLog == null || dispatchApplyLog.getState() != DispatchApplyLogConstant.DEFAULTSTATE) {
            throw new BaseException("apply.is.handle");
        }
        DispatchApplyLog dispatchApplyLogDB = new DispatchApplyLog();
        dispatchApplyLogDB.setApplyId(dispatchApplyLog.getApplyId());
        dispatchApplyLogDB.setHandleMsg(remark);
        dispatchApplyLogDB.setHandleTime(new Date());
        dispatchApplyLogDB.setState(state);
        dispatchApplyLogDB.setStockUserId(accountUser.getAccountUserId());
        dispatchApplyLogDB.setStockUserName(accountUser.getName());
        dispatchApplyLogDB.setApplyUserId(dispatchApplyLog.getApplyUserId());
        dispatchApplyLogDB.setHandleType(handleType);
        QueryWrapper editWrapper = new QueryWrapper();
        editWrapper.eq("applyId", dispatchApplyLog.getApplyId());
        //update(dispatchApplyLogDB, editWrapper);

        /**审核通过生成调度申请记录日志*/
        List<DispatchApplyMachine> applyMachines = dispatchApplyMachineService.list(editWrapper);
        /**申请通过*/
        if (state == DispatchApplyLogConstant.SUCCESSSTATE) {
            //批量查询申请设备的借出时间
            List<String> machineNOs = applyMachines.stream().map(DispatchApplyMachine::getMachineNO).collect(Collectors.toList());
            Map<String, Machine> machineMap = machineService.getByMachineNOs(machineNOs).stream().collect(Collectors.toMap(Machine::getMachineNO, Machine -> Machine));

            String UserCodeNotExist = null;
            String userDispatchMachineCache = RedisConstant.REDIS_BATCH_DISPATCH + dispatchApplyLog.getApplyUserId();
            for (DispatchApplyMachine machine : applyMachines) {

                /**判断是否已被他人调度*/
                if (redisService.get(RedisConstant.REDIS_TER_BIKE_DISPATCH + machine.getMachineNO()) == null) {
                    /**批量获取redis数据,区域*/
                    String areaDispatchMacheCache = RedisConstant.REDIS_BATCH_DISPATCH_AREA + machine.getAccountId();

                    /**查询车辆归属站点*/
                    ParkPoint parkPoint = parkPointService.getByMachineId(machine.getMachineId());
                    /**记录投放记录*/
                    DispatchLog dl = new DispatchLog();
                    dl.setAccountId(machine.getAccountId());
                    dl.setUserMainId(dispatchApplyLog.getApplyUserId());
                    dl.setMachineId(machine.getMachineId());
                    dl.setUserCode(machine.getUserCode());
                    dl.setMachineNO(machine.getMachineNO());
                    dl.setDispatchTime(DateTimeUtil.getNowTime());
                    dl.setOldParkPoint(parkPoint == null ? "" : parkPoint.getName());
                    dl.setOldParkPointId(parkPoint == null ? null: parkPoint.getParkPointId());
                    dl.setState(0);
                    dl.setIsValid(0);
                    dl.setMachineType(dataService.getMachine(machine.getMachineId()).getMachineType());
                    dl.setRegionId(dataService.getByGeoMachineId(machine.getMachineId()));
                    Machine nowMachine = machineMap.get(machine.getMachineNO());
                    if (Objects.nonNull(nowMachine)){
                        dl.setIdleTime(DateTimeUtil.getHours(nowMachine.getLastBorrowTime(), DateTimeUtil.getNowTime()));
                    }
                    redisService.add(RedisConstant.REDIS_TER_BIKE_DISPATCH + machine.getMachineNO(), JSONObject.toJSONString(dl), 3600 * 3);

                    /**批量获取redis数据,人员*/
                    List<String> userCode = new LinkedList<>();
                    try {
                        userCode = redisService.getList(userDispatchMachineCache);
                    } catch (SerializationException e) {
                        redisService.del(userDispatchMachineCache);
                    }
                    if (userCode == null || !userCode.contains(machine.getUserCode())) {
                        /**批量添加到redis中*/
                        redisService.andToList(userDispatchMachineCache, machine.getUserCode());
                    }

                    /**批量获取redis数据,区域*/
                    List<String> userCodeDispatch = new LinkedList<>();
                    try {
                        userCodeDispatch = redisService.getList(areaDispatchMacheCache);
                    } catch (SerializationException e) {
                        redisService.del(areaDispatchMacheCache);
                    }
                    if (userCodeDispatch == null || !userCodeDispatch.contains(machine.getUserCode())) {
                        /**批量添加到redis中*/
                        redisService.andToList(areaDispatchMacheCache, machine.getUserCode());
                    }

                    ParkPoint parkMachine = parkPointService.getByMachineId(machine.getMachineId());
                    redisService.add(RedisConstant.REDIS_TER_BIKE_DISPATCHANDMOVE + machine.getMachineId(), parkMachine == null ? "1" : parkMachine.getParkPointId().toString(), 3600 * 3);

                    redisService.add(RedisConstant.REDIS_TER_BIKE_DISPATCH_USER_CODE + machine.getUserCode(),machine.getMachineNO(), 24 * 3600);
                    /**添加redis申请记录缓存*/
                    redisService.add(RedisConstant.MACHINE_DISPATCH_APPLY_LOG + machine.getMachineNO(), dispatchApplyLogDB.getApplyUserId().toString(), 24 * 3600);
                } else {
                    UserCodeNotExist = UserCodeNotExist == null ? machine.getUserCode() : UserCodeNotExist + machine.getUserCode();
                }
            }
            if (UserCodeNotExist != null) {
                dispatchApplyLogDB.setRemark(UserCodeNotExist + WorkOrderConstant.exist_Dispatch);
            }
        }
        return update(dispatchApplyLogDB, editWrapper);
    }

    @Override
    public List<DispatchApplyLog> getByApplyIdDispatch(Integer applyId) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("applyId", applyId);
        List<DispatchApplyLog> dispatchApplyLogs = list(queryWrapper);
        return dispatchApplyLogs;
    }

    @Override
    public Integer checkMachineOccupy(AccountUser accountUser, Integer accountId) {
        AccountConfig accountConfig = dataService.getAccountConfig(accountId);
        Integer roleConfigValue = dataService.getRoleConfig(accountUser.getAccountUserType() + RoleConfigConstant.MAIN_LOCK_VEHICLE);

        if (roleConfigValue == null || !roleConfigValue.equals(1)) {
            return OCCUPY_NO_LIMIT;
        }

        if (accountConfig == null || accountConfig.getOccupyMax() == null || accountConfig.getOccupyMax().equals(0)) {
            return OCCUPY_NO_LIMIT;
        }

        Integer currentOccupy = machineOccupyService.getByAccountUserId(accountUser.getAccountUserId());

        Integer dispatchingOccupy = Optional.ofNullable(redisService.getList(RedisConstant.REDIS_BATCH_DISPATCH + accountUser.getAccountUserId()))
                .map(List::size).orElse(0);

        Integer available = accountConfig.getOccupyMax() - currentOccupy-dispatchingOccupy;

        return available < 0 ? 0 : available;
    }

}
